﻿using System;
using System.Runtime.InteropServices;

namespace ExRev.Engine
{
    /// <summary>
    /// Defines a two component vector.
    /// </summary>
    [Serializable, StructLayout(LayoutKind.Sequential)]
    public struct Vector2
        : IEquatable<Vector2>
    {
        /// <summary>
        /// The x-component of the vector.
        /// </summary>
        public float X;

        /// <summary>
        /// The y-component of the vector.
        /// </summary>
        public float Y;

        /// <summary>
        /// Returns a Vector2 with all of its components set to zero.
        /// </summary>
        public static Vector2 Zero
        {
            get { return zero; }
        }
        private static Vector2 zero;

        /// <summary>
        /// Returns a Vector2 with both of its components set to one.
        /// </summary>
        public static Vector2 One
        {
            get { return one; }
        }
        private static Vector2 one;

        /// <summary>
        /// Returns the unit vector for the x-axis.
        /// </summary>
        public static Vector2 UnitX
        {
            get { return unitX; }
        }
        private static Vector2 unitX;

        /// <summary>
        /// Returns the unit vector for the y-axis.
        /// </summary>
        public static Vector2 UnitY
        {
            get { return unitY; }
        }
        private static Vector2 unitY;

        /// <summary>
        /// Gets the size of the Vector2 type, in bytes.
        /// </summary>
        public static int SizeInBytes
        {
            get { return Marshal.SizeOf(typeof(Vector2)); }
        }

        /// <summary>
        /// Initializes a new instance of Vector2.
        /// </summary>
        /// <param name="x">Initial value for the x-component of the vector.</param>
        /// <param name="y">Initial value for the y-component of the vector.</param>
        public Vector2(float x, float y)
        {
            this.X = x;
            this.Y = y;
        }

        /// <summary>
        /// Creates a new instance of Vector2.
        /// </summary>
        /// <param name="value">Value to inizialize both components to.</param>
        public Vector2(float value)
        {
            X = Y = value;
        }

        static Vector2()
        {
            zero = new Vector2(0.0f, 0.0f);
            one = new Vector2(1.0f, 1.0f);
            unitX = new Vector2(1.0f, 0.0f);
            unitY = new Vector2(0.0f, 0.1f);
        }

        /// <summary>
        /// Retrieves a string representation of the current object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{{{X:{0} Y:{1}}}", new object[] {X.ToString(), Y.ToString()} );
        }

        /// <summary>
        /// Gets the hash code of the vector object.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() + Y.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified Object is equal to the Vector2.
        /// </summary>
        /// <param name="other">The Object to compare with the current Vector2.</param>
        /// <returns></returns>
        public bool Equals(Vector2 other)
        {
            return (this.X == other.X) && (this.Y == other.Y);
        }

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">Object to make the comparison with.</param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            return (obj is Vector2) ? Equals((Vector2)obj) : false;
        }

        /// <summary>
        /// Calculates the length of the vector.
        /// </summary>
        /// <returns></returns>
        public float Length()
        {
            float lengthSquared = (X * X) + (Y * Y);
            return (float)Math.Sqrt(lengthSquared);
        }

        /// <summary>
        /// Calculates the length of the vector squared.
        /// </summary>
        /// <returns></returns>
        public float LengthSquared()
        {
            return (X * X) + (Y * Y);
        }

        /// <summary>
        /// Calculates the distance between two vectors.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static float Distance(Vector2 value1, Vector2 value2)
        {
            float distanceX = value1.X - value2.X;
            float distanceY = value1.Y - value2.Y;
            float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
            return (float)Math.Sqrt(distanceSquared);
        }

        /// <summary>
        /// Calculates the distance between two vectors squared.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static float DistanceSquared(Vector2 value1, Vector2 value2)
        {
            float distanceX = value1.X - value2.X;
            float distanceY = value1.Y - value2.Y;
            return (distanceX * distanceX) + (distanceY * distanceY);
        }

        /// <summary>
        /// Calculates the dot product of two vectors. If the two vectors are unit vectors, the dot product returns a floating point value between
        /// -1 and 1 that can be used to determine some properties of the angle between two vectors. For example, it can show whether the vectors are
        /// orthogonal, parallel, or have an acute or obtuse angle between them.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static float Dot(Vector2 value1, Vector2 value2)
        {
            return (value1.X * value2.X) + (value1.Y * value2.Y);
        }

        /// <summary>
        /// Turn the current vector into a unit vector. The result is a vector one unit in length pointing in the same direction as the original vector.
        /// </summary>
        public void Normalize()
        {
            float lengthSquared = (X * X) + (Y * Y);
            float lengthInverted = 1.0f / (float)Math.Sqrt(lengthSquared);
            X *= lengthInverted;
            Y *= lengthInverted;
        }

        /// <summary>
        /// Creates a unit vector from the specified vector. The result is a vector one unit in length pointing in the same direction as the original vector.
        /// </summary>
        /// <param name="vector">Source vector.</param>
        /// <returns></returns>
        public static Vector2 Normalize(Vector2 vector)
        {
            float lengthSquared = (vector.X * vector.X) + (vector.Y * vector.Y);
            float lengthInverted = 1.0f / (float)Math.Sqrt(lengthSquared);
            return new Vector2(vector.X * lengthInverted, vector.Y * lengthInverted);
        }      

        /// <summary>
        /// Determines the reflect vector of the given vector and normal.
        /// </summary>
        /// <param name="vector">Source vector.</param>
        /// <param name="normal">Normal of vector.</param>
        /// <returns></returns>
        public static Vector2 Reflect(Vector2 vector, Vector2 normal)
        {
            float dotDoubled = 2 * (vector.X * vector.X) + (vector.Y * normal.Y);
            return new Vector2(vector.X - dotDoubled * normal.X, vector.Y - dotDoubled * normal.Y);
        }

        /// <summary>
        /// Returns a vector that contains the lowest value from each matching pair of components.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static Vector2 Min(Vector2 value1, Vector2 value2)
        {
            return new Vector2(value1.X < value2.X ? value1.X : value2.X, value1.Y < value2.Y ? value1.Y : value2.Y);
        }

        /// <summary>
        /// Returns a vector that contains the highest value from each matching pair of components.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static Vector2 Max(Vector2 value1, Vector2 value2)
        {
            return new Vector2(value1.X > value2.X ? value1.X : value2.X, value1.Y > value2.Y ? value1.Y : value2.Y);
        }

        /// <summary>
        /// Restricts a value to be within a specified range.
        /// </summary>
        /// <param name="value">The value to clamp.</param>
        /// <param name="min">The minimum value.</param>
        /// <param name="max">The maximum value.</param>
        /// <returns></returns>
        public static Vector2 Clamp(Vector2 value, Vector2 min, Vector2 max)
        {
            float x = value.X;
            x = x > max.X ? max.X : x;
            x = x < min.X ? min.X : x;
            float y = value.Y;
            y = y > max.Y ? max.Y : y;
            y = y < min.Y ? min.Y : y;
            return new Vector2(x, y);
        }

        /// <summary>
        /// Performs a linear interpolation between two vectors.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <param name="amount">Value between 0 and 1 indicating the weight of value2.</param>
        /// <returns></returns>
        public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount)
        {
            return new Vector2(value1.X + (value2.X - value1.X) * amount,
                               value1.Y + (value2.Y - value1.Y) * amount);
        }

        /// <summary>
        /// Interpolates between two values using a cubic equation.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <param name="amount">Weighting value.</param>
        /// <returns></returns>
        public static Vector2 SmoothStep(Vector2 value1, Vector2 value2, float amount) 
        {
            amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount);
            amount = (amount * amount) * (3.0f - (2.0f * amount));
            return new Vector2(value1.X + ((value2.X - value1.X) * amount),
                               value1.Y + ((value2.Y - value2.Y) * amount));
        }

        /// <summary>
        /// Performs a Hermite spline interpolation.
        /// </summary>
        /// <param name="value1">Source position vector.</param>
        /// <param name="tangent1">Source tangent vector.</param>
        /// <param name="value2">Source position vector.</param>
        /// <param name="tangent2">Source tangent vector.</param>
        /// <param name="amount">Weighting factor.</param>
        /// <returns></returns>
        public static Vector2 Hermite(Vector2 value1, Vector2 tangent1, Vector2 value2, Vector2 tangent2, float amount)
        {
            float amountSquared = amount * amount;
            float amountCubed = amountSquared * amount;
            float weight1 = ((2.0f * amountCubed) - (3.0f * amountSquared)) + 1.0f;
            float weight2 = (-2.0f * amountCubed) + 3.0f * amountSquared;
            float tangentWeight1 = (amountCubed - (2.0f * amountSquared)) + amount;
            float tangentWeight2 = amountCubed - amountSquared;
            return new Vector2((value1.X * weight1) + (value2.X * weight2) + (tangent1.X * tangentWeight1) + (tangent2.X * tangentWeight2),
                               (value1.Y * weight1) + (value2.Y * weight2) + (tangent1.Y * tangentWeight1) + (tangent2.Y * tangentWeight2));
        }

        /// <summary>
        /// Performs a Catmull-Rom interpolation using the specified positions.
        /// </summary>
        /// <param name="value1">The first position in the interpolation.</param>
        /// <param name="value2">The second position in the interpolation.</param>
        /// <param name="value3">The third position in the interpolation.</param>
        /// <param name="value4">The fourth position in the interpolation.</param>
        /// <param name="amount">Weighting factor.</param>
        /// <returns></returns>
        public static Vector2 CatmullRom(Vector2 value1, Vector2 value2, Vector2 value3, Vector2 value4, float amount)
        {
            float amountSquared = amount * amount;
            float amountCubed = amountSquared * amount;
            return new Vector2(0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * amountSquared)) + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * amountCubed)),
                               0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * amountSquared)) + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * amountCubed)));
        }

        /// <summary>
        /// Returns a Vector2 containing the 2D Cartesian coordinates of a point specified in a barycentric (areal) coordinates relative to a 2D triangle.
        /// </summary>
        /// <param name="value1">A Vector2 containing the 2D Cartesian coordinates of vertex 1 of the triangle.</param>
        /// <param name="value2">A Vector2 containing the 2D Cartesian coordinates of vertex 2 of the triangle.</param>
        /// <param name="value3">A Vector2 containing the 2D Cartesian coordinates of vertex 3 of the triangle.</param>
        /// <param name="amount1">Barycentric coordinate b2, which expresses the weighhing factor toward vertex 2.</param>
        /// <param name="amount2">Barycentric coordinate b3, which expresses the weighting factor toward vertex 3.</param>
        /// <returns></returns>
        public static Vector2 Barycentric(Vector2 value1, Vector2 value2, Vector2 value3, float amount1, float amount2)
        {
            return new Vector2(value1.X + (amount1 * (value2.X - value1.X)) + (amount2 * (value3.X - value1.X)),
                               value1.Y + (amount1 * (value2.Y - value1.Y)) + (amount2 * (value3.Y - value1.Y)));
        }

        /// <summary>
        /// Transforms the vector (x, y, 0, 1) by the specified matrix.
        /// </summary>
        /// <param name="position">Source vector.</param>
        /// <param name="matrix">Transformation Matrix.</param>
        /// <returns></returns>
        public static Vector2 Transform(Vector2 position, Matrix matrix)
        {
            float x = (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41;
            float y = (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42;
            return new Vector2(x, y);
        }

        /// <summary>
        /// Transforms a 2D vector normal by a matrix.
        /// </summary>
        /// <param name="normal">Source vector.</param>
        /// <param name="matrix">Transformation Matrix.</param>
        /// <returns></returns>
        public static Vector2 TransformNormal(Vector2 normal, Matrix matrix)
        {
            float x = (normal.X * matrix.M11) + (normal.Y * matrix.M21);
            float y = (normal.X * matrix.M12) + (normal.Y * matrix.M22);
            return new Vector2(x, y);
        }

        /// <summary>
        /// Transform a single Vector2, or the vector normal (x, y, 0, 0), by a specified Quaternion rotation.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="rotation"></param>
        /// <returns></returns>
        public static Vector2 Transform(Vector2 value, Quaternion rotation)
        {
            float x = rotation.X + rotation.X;
            float y = rotation.Y + rotation.Y;
            float z = rotation.Z + rotation.Z;
            float wz = rotation.W * z;
            float xx = rotation.X * x;
            float xy = rotation.X * y;
            float yy = rotation.Y * y;
            float zz = rotation.Z * z;
            return new Vector2((value.X * ((1.0f - yy) - zz)) + (value.Y * (xy - wz)),
                               (value.X * (xy + wz)) + (value.Y * ((1.0f - xx) - zz)));            
        }

        /// <summary>
        /// Transforms an array of Vector2s by a specified matrix.
        /// </summary>
        /// <param name="sourceArray">Array of Vector2s to transform.</param>
        /// <param name="matrix">Transformation Matrix to apply.</param>
        /// <param name="destinationArray">An existing array into which the transformed Vector2s are written.</param>
        public static void Transform(Vector2[] sourceArray, Matrix matrix, Vector2[] destinationArray)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (destinationArray.Length < sourceArray.Length) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            for (int i = 0; i < sourceArray.Length; i++) {
                float x = sourceArray[i].X;
                float y = sourceArray[i].Y;
                destinationArray[i].X = (x * matrix.M11) + (y * matrix.M21) + matrix.M41;
                destinationArray[i].Y = (x * matrix.M12) + (y * matrix.M22) + matrix.M42;
            }
        }

        /// <summary>
        /// Transforms a specified range on an array of Vector2s by a specified Matrix and places the results in a specified range in a destination array.
        /// </summary>
        /// <param name="sourceArray">Source array.</param>
        /// <param name="sourceIndex">Index of the first Vector2 to transform in the source array.</param>
        /// <param name="matrix">Matrit to transformed by.</param>
        /// <param name="destinationArray">Destination array into which the resulting Vector2s will be written.</param>
        /// <param name="destinationIndex">Index of the position in the destination array where the first restult Vector2 should be written.</param>
        /// <param name="length">Number of Vector2s to be transformed.</param>
        public static void Transform(Vector2[] sourceArray, int sourceIndex, Matrix matrix, Vector2[] destinationArray, int destinationIndex, int length)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (sourceArray.Length < sourceIndex + length) throw new ArgumentException("Source array must be equal or bigger than requested length.");
            if (destinationArray.Length < (destinationIndex + length)) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            while (length > 0) {
                float x = sourceArray[sourceIndex].X;
                float y = sourceArray[sourceIndex].Y;
                destinationArray[destinationIndex].X = (x * matrix.M11) + (y * matrix.M21) + matrix.M41;
                destinationArray[destinationIndex].Y = (x * matrix.M12) + (y * matrix.M22) + matrix.M42;
                sourceIndex++;
                destinationIndex++;
                length--;
            }
        }

        /// <summary>
        /// Transforms a 2D vector normal by a matrix.
        /// </summary>
        /// <param name="sourceArray">Source vector.</param>
        /// <param name="matrix">Transformation Matrix.</param>
        /// <param name="destinationArray"></param>
        public static void TransformNormal(Vector2[] sourceArray, Matrix matrix, Vector2[] destinationArray)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (destinationArray.Length < sourceArray.Length) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            for (int i = 0; i < sourceArray.Length; i++) {
                float x = sourceArray[i].X;
                float y = sourceArray[i].Y;
                destinationArray[i].X = (x * matrix.M11) + (y * matrix.M21);
                destinationArray[i].Y = (x * matrix.M12) + (y * matrix.M22);
            }
        }

        /// <summary>
        /// Transforms a specified range in an array of Vector2 vector normals by a specified Matrix and places the results in a specific range in a destination array.
        /// </summary>
        /// <param name="sourceArray">Source array.</param>
        /// <param name="sourceIndex">Index of the first Vector2 to transform in the source array.</param>
        /// <param name="matrix">Matrix to apply.</param>
        /// <param name="destinationArray">Destination array into which the resulting Vector2s are written.</param>
        /// <param name="destinationIndex">Index of the position in the destination array where the first result Vector2 should be written.</param>
        /// <param name="length">Number of vector normals to be transformed.</param>
        public static void TransformNormal(Vector2[] sourceArray, int sourceIndex, Matrix matrix, Vector2[] destinationArray, int destinationIndex, int length)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (sourceArray.Length < sourceIndex + length) throw new ArgumentException("Source array must be equal or bigger than requested length.");
            if (destinationArray.Length < (destinationIndex + length)) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            while (length > 0) {
                float x = sourceArray[sourceIndex].X;
                float y = sourceArray[sourceIndex].Y;
                destinationArray[destinationIndex].X = (x * matrix.M11) + (y * matrix.M21);
                destinationArray[destinationIndex].Y = (x * matrix.M12) + (y * matrix.M22);
                sourceIndex++;
                destinationIndex++;
                length--;
            }
        }

        /// <summary>
        /// Transform an array of Vector2s by a specified Quaternion.
        /// </summary>
        /// <param name="sourceArray">Array ov Vector2s to transform.</param>
        /// <param name="rotation">The rotation Quaternion to use.</param>
        /// <param name="destinationArray">An existing array into which the transformed Vector2s are written.</param>
        public static void Transform(Vector2[] sourceArray, Quaternion rotation, Vector2[] destinationArray)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (destinationArray.Length < sourceArray.Length) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            float x = rotation.X + rotation.X;
            float y = rotation.Y + rotation.Y;
            float z = rotation.Z + rotation.Z;
            float wz = rotation.W * z;
            float xx = rotation.X * x;
            float xy = rotation.X * y;
            float yy = rotation.Y * y;
            float zz = rotation.Z * z;
            float num1 = (1f - yy) - zz;
            float num2 = xy - wz;
            float num3 = xy + wz;
            float num4 = (1f - xx) - zz;
            for (int i = 0; i < sourceArray.Length; i++) {
                float sx = sourceArray[i].X;
                float sy = sourceArray[i].Y;
                destinationArray[i].X = (sx * num1) + (sy * num2);
                destinationArray[i].Y = (sx * num3) + (sy * num4);
            }
        }

        /// <summary>
        /// Transforms a specified range in an array of Vector2s by a specified Quaternion and places the results in a specified range in a destination array.
        /// </summary>
        /// <param name="sourceArray">Source array.</param>
        /// <param name="sourceIndex">Index of the first Vector2 to transform in the source array.</param>
        /// <param name="rotation">Quaternion rotation to apply.</param>
        /// <param name="destinationArray">Destination array into which the resulting Vector2s are written.</param>
        /// <param name="destinationIndex">Index of the position in the destination array where the first Vector2 should be written.</param>
        /// <param name="length">Number of Vector2s to be transformed.</param>
        public static void Transform(Vector2[] sourceArray, int sourceIndex, Quaternion rotation, Vector2[] destinationArray, int destinationIndex, int length)
        {
            if (sourceArray == null) throw new ArgumentNullException("sourceArray");
            if (destinationArray == null) throw new ArgumentNullException("destinationArray");
            if (sourceArray.Length < sourceIndex + length) throw new ArgumentException("Source array must be equal or bigger than requested length.");
            if (destinationArray.Length < (destinationIndex + length)) throw new ArgumentException("Destination array must be equal or bigger than requested length.");
            float x = rotation.X + rotation.X;
            float y = rotation.Y + rotation.Y;
            float z = rotation.Z + rotation.Z;
            float wz = rotation.W * z;
            float xx = rotation.X * x;
            float xy = rotation.X * y;
            float yy = rotation.Y * y;
            float zz = rotation.Z * z;
            float num1 = (1f - yy) - zz;
            float num2 = xy - wz;
            float num3 = xy + wz;
            float num4 = (1f - xx) - zz;
            while (length > 0) {
                float sx = sourceArray[sourceIndex].X;
                float sy = sourceArray[sourceIndex].Y;
                destinationArray[destinationIndex].X = (sx * num1) + (sy * num2);
                destinationArray[destinationIndex].Y = (sx * num3) + (sy * num4);
                sourceIndex++;
                destinationIndex++;
                length--;
            }
        }

        /// <summary>
        /// Tests vector for equality.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static bool operator==(Vector2 value1, Vector2 value2)
        {
            return (value1.X == value2.X) && (value1.Y == value2.Y);
        }

        /// <summary>
        /// Tests vectors for inequality.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static bool operator!=(Vector2 value1, Vector2 value2)
        {
            return (value1.X != value2.X) || (value1.Y != value2.X);
        }

        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static Vector2 operator+(Vector2 value1, Vector2 value2)
        {
            return new Vector2(value1.X + value2.X, value1.Y + value2.Y);
        }

        /// <summary>
        /// Subtracts a vector from a vector.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Source vector.</param>
        /// <returns></returns>
        public static Vector2 operator-(Vector2 value1, Vector2 value2)
        {
            return new Vector2(value1.X - value2.X, value1.Y - value2.Y);
        }

        /// <summary>
        /// Returns a vector pointing in the opposite direction.
        /// </summary>
        /// <param name="value">Source vector.</param>
        /// <returns></returns>
        public static Vector2 operator -(Vector2 value)
        {
            return new Vector2(-value.X, -value.Y);
        }

        /// <summary>
        /// Multiplies the components of two vectors by each other.
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static Vector2 operator*(Vector2 value1, Vector2 value2) 
        {
            return new Vector2(value1.X * value2.X, value1.Y * value2.Y);
        }

        /// <summary>
        /// Multiplies a vector by a scalar value.
        /// </summary>
        /// <param name="value">Source vector.</param>
        /// <param name="scaleFactor">Svalar value.</param>
        /// <returns></returns>
        public static Vector2 operator*(Vector2 value, float scaleFactor)
        {
            return new Vector2(value.X * scaleFactor, value.Y * scaleFactor);
        }

        /// <summary>
        /// Multiplies a vector by a scalar.
        /// </summary>
        /// <param name="scaleFactor">Scalar value.</param>
        /// <param name="value">Source vector.</param>
        /// <returns></returns>
        public static Vector2 operator*(float scaleFactor, Vector2 value)
        {
            return new Vector2(value.X * scaleFactor, value.Y * scaleFactor);
        }

        /// <summary>
        /// Divides the components of a vector by the components of another vector.
        /// </summary>
        /// <param name="value1">Source vector.</param>
        /// <param name="value2">Divisor vector.</param>
        /// <returns></returns>
        public static Vector2 operator/(Vector2 value1, Vector2 value2)
        {
            return new Vector2(value1.X / value2.X, value1.Y / value2.Y);
        }

        /// <summary>
        /// Divides a vector by a scalar value.
        /// </summary>
        /// <param name="value">Source vector.</param>
        /// <param name="divider">The divisor.</param>
        /// <returns></returns>
        public static Vector2 operator/(Vector2 value, float divider)
        {
            float scaleFactor = 1.0f / divider;
            return new Vector2(value.X * scaleFactor, value.Y * scaleFactor);
        }
    }
}
